from control.order_control import Order, OrderType
from utils.crawl_tool_for_py3_v6 import crawlerTool
from worker.base.base_worker import BaseWorker
import constant.param_config as config
from constant.globl_constant import eventTag, eventBeforeTag, eventSignalTag
from utils.log_util import prints, saveLog
from utils.time_util import current_time, stamp_time, fmt_d, gmt_2_local, DATE_FORMAT, YYYYMMDDHHMMSS
from utils.e_mail import sendMail, cleanMail
import json
import datetime
import threading
import time


class CalendarWorker(BaseWorker):
    # calendarUrl = "http://vip.stock.finance.sina.com.cn/forex/api/jsonp.php/SINAFINANCE159169506763295067/DailyFX_AllService.getOnedayEventsNew?date=2020-06-09&rn=1591695067632"
    baseUrl = "https://cdn-rili.jin10.com/web_data/"

    nextDataList = []
    nextEventList = []
    calendarValue = []
    eventValue = []
    priceThread = None

    lastForbidTime = 0
    lastForbidName = ""

    timeSleep = 10
    lastDay = -1

    def __init__(self):
        super().__init__()
        self.request = crawlerTool()

    def startThread(self):
        thread = threading.Thread(target=self.execution)
        # thread.daemon = True  # 设置线程为守护线程，以便在主程序退出时自动结束线程
        thread.start()

    # def execution(self):
    #     """
    #     检测日历事件
    #     """

    #     while True:
    #         timeNow = datetime.datetime.now().day
    #         if self.lastDay != timeNow:
    #             self.lastDay = timeNow
    #             self.calendarValue, self.eventValue = self.getRequestResult()
    #             self.calendarValue = [
    #                 item for item in self.calendarValue if item["star"] >= getImportance() and item["actual"] is None and item["time_status"] != "待确定"]
    #             msg = "\n".join([self.calendarItemInfo(item)
    #                             for item in self.calendarValue])
    #             sendMail("日内重要事件总览，请查看", msg)
    #         else:
    #             self.eventInfo()
    #         # time.sleep(60 * 10)
    #         time.sleep(self.timeSleep)

    created = False

    def execution(self):
        """
        检测日历事件
        """

        prints("事件监测服务已启动...")

        while True:
            if self.isLogin and self.marketClose() is False:
                timeNow = datetime.datetime.now().day
                if self.lastDay != timeNow:
                    self.lastDay = timeNow
                    # 每日零点邮件清理
                    cleanMail()
                    self.dayEvent()
                else:
                    if len(self.calendarValue) != 0:
                        # nextDataList 将要公布事件为空时获取最近一条事件
                        if len(self.nextDataList) == 0:
                            self.nearEvent()
                        else:
                            self.newData, self.newEvent = self.getEventInfo()
                            self.eventInfo()
                            self.calendarTimer()

                # 日内重要事件检测
                self.importantCheck()
            time.sleep(self.timeSleep)

    def dayEvent(self):
        """
        获取日内事件
        """
        self.nextDataList.clear()
        self.nextEventList.clear()
        self.calendarValue.clear()

        calendar, event = self.getRequestResult()

        if calendar is None or len(calendar) == 0:
            return

        self.calendarValue = [
            item for item in calendar
            if item["star"] >= config.getImportance()
            and item["actual"] is None and item["time_status"] != "待确定"
        ]

        if len(self.calendarValue) == 0:
            sendMail("日内重要事件总览，请查看", "当前暂无重要事件", timer=False)
            return

        msg = "\n".join(
            [self.calendarItemInfo(item) for item in self.calendarValue])
        sendMail("日内重要事件总览，请查看", msg, timer=False)

    def importantCheck(self):
        """
        日内重要事件监测
        """

        agoTime = config.getImportAgoTime()
        nowTime = current_time()

        if self.calendarValue != None and len(self.calendarValue) != 0:
            itemList = [
                item for item in self.calendarValue
                if item["name"] in config.getCalendarList()
            ]

            for item in itemList:
                name = item["name"]
                pubTime = item["pub_time_unix"]
                actual = item["actual"]
                star = item["star"]

                if pubTime is not None and pubTime != "" and actual is None:
                    # 重要事件发布前后N小时禁止交易
                    if pubTime - nowTime < agoTime and name != self.lastForbidName:
                        self.lastForbidTime = pubTime
                        self.lastForbidName = name

                        BaseWorker.forbidDeal = True
                        BaseWorker.eventShow = False
                        saveLog("稍后有重要事件公布，将暂停日内交易")
                        return

        # 重要事件发布前后N小时禁止交易
        if self.lastForbidTime != 0 and nowTime > self.lastForbidTime + agoTime / 2:
            saveLog("超过事件交易限制时间，已恢复交易")
            self.lastForbidTime = 0
            self.lastForbidName = ""
            BaseWorker.forbidDeal = False

    def eventInfo(self):
        """
        获取事件详情
        """
        pubTime = self.nextDataList[0]["pub_time_unix"]
        nowTime = current_time()

        # 未发布事件在30s后发布，或者已发布但是没有获取到数据
        if pubTime - nowTime < 30 or pubTime < nowTime:
            # 有新的数据发布时判断是否在交易计划内，没有数据发布时每2s请求一次
            if len(self.newData) != 0:
                self.nextDataList.clear()
                self.nextEventList.clear()
                # 处理交易
                self.calendarTrade(self.newData, self.newEvent)
            else:
                self.timeSleep = 2

    def calendarTimer(self):
        """
        计时器
        """

        pubTime = 0
        eventState = 0  # 1:不在计划内的数据     2:在计划内的数据
        nowTime = current_time()

        nextEvent = [
            item for item in self.nextDataList
            if item["name"] in [child for child in config.getCalendarList()]
        ]

        # 在指定时间前开多空单
        if len(nextEvent) == 0 and len(self.nextDataList) != 0:
            pubTime = self.nextDataList[0]["pub_time_unix"]
            eventState = 1
        else:
            if len(nextEvent) != 0:
                pubTime = nextEvent[0]["pub_time_unix"]
                eventState = 2

        if config.getCalendarTrade() is False:
            return

        if pubTime != 0 and eventState != 0 and pubTime - nowTime < config.getAheadOrderTime(
        ):
            if self.newData is None or len(self.newData) == 0:
                if eventState == 2 and self.hasTagOrder() is False:

                    sellPrice = self.control.getCurrentPrice(
                        self.symble, orderType=Order.CREATE_SELL)
                    buyPrice = self.control.getCurrentPrice(
                        self.symble, orderType=Order.CREATE_BUY)

                    # 针对计划内数据下单
                    self.control.createOrder(OrderType.BUY,
                                             self.symble,
                                             priceSp=buyPrice - 3,
                                             priceTp=buyPrice + 5,
                                             lots=self.getOrderLots(),
                                             remark=eventBeforeTag)
                    self.control.createOrder(OrderType.SELL,
                                             self.symble,
                                             priceSp=sellPrice + 3,
                                             priceTp=sellPrice - 5,
                                             lots=self.getOrderLots(),
                                             remark=eventBeforeTag)
                elif eventState == 1:
                    if self.priceThread == None:
                        self.priceThread = threading.Thread(
                            target=self.priceFluctuate)
                        self.priceThread.start()

    def getOrderLots(self):
        """
        下单手数
        """
        balance = self.getAccountMoney()
        if balance <= 30:
            return 0.01
        return 0

    def priceFluctuate(self):
        """
        瞬时价格波动
        """

        beforeEventPrice = self.control.getCurrentPrice(self.symble)
        saveLog("价格波动检测中...")

        timeNum = 0
        while True:
            eventPrice = self.control.getCurrentPrice(self.symble)
            if timeNum < 3 * 60:
                if eventPrice - beforeEventPrice > config.getMomentRange(
                ):  # 上涨
                    # 在阻力区间之内
                    if self.passRound(Order.CREATE_BUY, eventPrice,
                                      level=2) is False:
                        saveLog("重要数据公布时价格波动剧烈，已下多单")
                        sendMail("价格波动较大已下多单", "重要数据公布时价格波动剧烈，已下多单")
                        self.control.createOrder(OrderType.BUY,
                                                 self.symble,
                                                 priceSp=eventPrice - 3,
                                                 lots=self.getOrderLots(),
                                                 remark=eventSignalTag)
                    self.priceThread = None
                    break
                elif beforeEventPrice - eventPrice > config.getMomentRange(
                ):  # 下跌
                    # 在阻力区间之内
                    if self.passRound(Order.CREATE_SELL, eventPrice,
                                      level=2) is False:
                        saveLog("重要数据公布时价格波动剧烈，已下空单")
                        sendMail("价格波动较大已下空单", "重要数据公布时价格波动剧烈，已下空单")
                        self.control.createOrder(OrderType.SELL,
                                                 self.symble,
                                                 priceSp=eventPrice + 3,
                                                 lots=self.getOrderLots(),
                                                 remark=eventSignalTag)
                    self.priceThread = None
                    break
            else:
                saveLog("重要数据公布，价格波动无效，禁止下单")
                self.priceThread = None
                break
            timeNum = timeNum + 1
            time.sleep(1)

    def nearEvent(self):
        """
        获取最近一条未发布事件
        """
        self.timeSleep = 10

        importance = config.getImportance()
        noticeSpace = config.getCalendarTime()
        currentTime = current_time()
        timeSpace = currentTime + noticeSpace

        # 对 self.calendarValue 进行重新过滤
        self.calendarValue = [
            item for item in self.calendarValue if item["star"] >= importance
            and item["actual"] is None and item["time_status"] != "待确定"
        ]

        if len(self.calendarValue) != 0:
            for item in self.calendarValue:
                pubTime = item["pub_time_unix"]
                actual = item["actual"]
                star = item["star"]
                if pubTime is not None and pubTime != "" and actual is None and star >= importance:
                    # 将未来特定时间内要公布的数据添加到 nextDataList
                    if pubTime > timeSpace and pubTime < timeSpace + 5 * 60:
                        self.nextDataList.append(item)

        if len(self.eventValue) != 0:
            for item in self.eventValue:
                time = item["event_time"]
                determine = item["determine"]
                star = item["star"]
                if time is not None and time != "" and star >= importance:
                    gmt = gmt_2_local(
                        fmt_d(stamp_time(time, fmt_str=DATE_FORMAT)))
                    timeEvent = stamp_time(gmt)
                    if timeEvent > timeSpace and timeEvent < timeSpace + 5 * 60:
                        self.nextEventList.append(item)

        if len(self.nextDataList) != 0:
            self.calendarEmail()

    def getEventInfo(self):
        """
        获取事件详情
        """
        newData = []
        newEvent = []

        calendarList, eventList = self.getRequestResult()
        if calendarList is not None and len(calendarList) != 0:
            newData = [
                item for item in calendarList if item["actual"] is not None and
                item["name"] in [child["name"] for child in self.nextDataList]
            ]

        if eventList is not None and len(eventList) != 0:
            newEvent = [
                item for item in eventList if item["event_content"] in
                [child["event_content"] for child in self.nextEventList]
            ]
        return newData, newEvent

    def hasLimitOrder(self):
        """
        是否存在挂单
        """
        allLimitOrder = self.getLimitOrderDetail(symbol=self.symble)
        orderList = [
            item for item in allLimitOrder if eventTag in item.comment
        ]
        return True if len(orderList) != 0 else False

    def hasTagOrder(self):
        """
        标记当前下单
        """
        allOrder = self.getPositionsDetail(symbol=self.symble)
        orderList = [item for item in allOrder if eventTag in item.comment]
        return True if len(orderList) != 0 else False

    def calendarEmail(self):
        """
        发送日历邮件通知
        """
        eventTitle = str(int(config.getCalendarTime() / 60)) + "分钟后有重要数据公布"
        eventMsg = "经济数据：\n"
        for item in self.nextDataList:
            eventMsg = eventMsg + item["name"] + "\n"

        eventMsg = eventMsg + "\n事件：\n"

        if len(self.nextEventList) != 0:
            if item in self.nextEventList:
                eventMsg = eventMsg + item["event_content"] + "\n"
        else:
            eventMsg = eventMsg + "无"

        saveLog(eventTitle)
        sendMail(eventTitle, eventMsg)

    def calendarTrade(self, data, event):
        """
        日历交易
        """

        BaseWorker.eventShow = True
        mailMsg = ""

        for item in data:
            name = item["name"]
            previous = item["previous"]
            consensus = item["consensus"]
            actual = item["actual"]
            self.dealTrade(name, previous, consensus, actual)

            if name is not None and name != "":
                if previous is None or previous == "":
                    previous = "无"
                if consensus is None or consensus == "":
                    consensus = "无"
                if actual is None or actual == "":
                    actual = "无"
                mailMsg = mailMsg + "\n" + name + ":  前值：" + \
                    previous + "；预测值：" + consensus + "；公布值：" + actual
        saveLog("重要数据公布如下：\n" + mailMsg)
        sendMail("重要数据已公布，请及时查看", mailMsg)

    def dealTrade(self, name, previous, consensus, actual):
        """
        处理交易
        """

        # 判断日历交易是否开启，以及当前事件是否是允许交易事件
        if config.getCalendarTrade() and name in config.getCalendarList():
            previousValue = 0
            consensusValue = 0
            actualValue = 0
            if previous != "" and previous is not None:
                previousValue = float(previous)

            if consensus != "" and consensus is not None:
                consensusValue = float(consensus)

            if actual != "" and actual is not None:
                actualValue = float(actual)

            if actualValue != 0 and consensusValue != 0:
                bidPrice = self.control.getCurrentPrice(
                    self.symble, Order.CREATE_BUY)
                askPrice = self.control.getCurrentPrice(
                    self.symble, Order.CREATE_SELL)

                if name == "当周初请失业金人数":
                    if actualValue < consensusValue:
                        self.closeOrder(Order.CLOSE_BUY)
                        self.createCalendarOrder(OrderType.SELL, askPrice + 2,
                                                 "请失业金人数公布数据低于预期，看空黄金")
                    elif actualValue > consensusValue:
                        self.closeOrder(Order.CLOSE_SELL)
                        self.createCalendarOrder(OrderType.BUY, bidPrice - 2,
                                                 "请失业金人数公布数据高于预期，看涨黄金")
                elif name == "季调后非农就业人口":
                    if actualValue < consensusValue:
                        self.closeOrder(Order.CLOSE_SELL)
                        self.createCalendarOrder(OrderType.BUY, bidPrice - 2,
                                                 "非农就业人口公布数据低于预期，看涨黄金")
                    elif actualValue > consensusValue:
                        self.closeOrder(Order.CLOSE_BUY)
                        self.createCalendarOrder(OrderType.SELL, askPrice + 2,
                                                 "非农就业人口公布数据高于预期，看空黄金")

    def closeOrder(self, state):
        """
        关闭订单
        """
        for item in self.getOrderForType(state):
            if eventBeforeTag in item.comment:
                saveLog("数据已公布，关闭数据前下单，ticket：" + str(item.ticket))
                self.control.closeOrder(item)

    def createCalendarOrder(self, orderType, sp, log):
        """
        创建订单
        """
        saveLog(log)
        sendMail(log)
        self.control.createOrder(orderType,
                                 self.symble,
                                 priceSp=sp,
                                 lots=self.getOrderLots(),
                                 remark=eventTag)

    def calendarItemInfo(self, item):
        """
        日历字符拼接
        """
        standard = 7
        starStr = item["star"] * "*" + (standard - item["star"]) * " "
        itemInfo = fmt_d(item["pub_time_unix"], YYYYMMDDHHMMSS) + "   "+starStr + \
            "  "+item["country"]+item["name"]
        return itemInfo

    def getRequestResult(self):
        """
        接口返回
        """

        calendar = None
        event = None
        calendarUrl, eventUrl = self.getUrl()
        calendarResult = self.request.get(calendarUrl)
        eventResult = self.request.get(eventUrl)

        if calendarResult is not None:
            calendarList = calendarResult.decode("utf-8")
            calendar = json.loads(calendarList)

        if eventResult is not None:
            eventList = eventResult.decode("utf-8")
            event = json.loads(eventList)
        return calendar, event

    def getUrl(self):
        """
        url
        """
        date = datetime.datetime.now()
        year = str(date.year)
        month = str(date.month)
        day = str(date.day)

        if date.day < 10:
            day = "0" + str(date.day)

        if date.month < 10:
            month = "0" + str(date.month)

        calendar = self.baseUrl+year+"/daily/" + \
            month + "/" + day + "/economics.json"
        event = self.baseUrl + year + "/daily/" + month + "/" + day + "/event.json"
        return calendar, event
